Gut, ich möchte jetzt abschließend ein kurzes Beispiel bringen von einem Datenringpuffer,
den wir jetzt mal so als Monitor formuliert sehen wollen. Das ist ein Beispiel, was wir aus einer
früheren Lehrveranstaltung schon kennen, wo wir das schon eingeführt haben und dann ja auf der
C-Sprachen-Ebene geblieben sind. Hier wollen wir mal sozusagen eine fiktive Programmiersprache
annehmen, nämlich Concurrent C++. Diese Sprache gibt es nicht wirklich. Hier sehen wir eben, das
ist eine objektorientierte Sprache. Java ähnlich, wir haben ja Klassen, Class Buffer beschreibt
dann also praktisch den Blueprint unserer Pufferimplementierung als Ringpuffer ausgelegt. Wir
geben hier, machen aus einer Template-Klasse, was bedeutet, dass wir dann halt als zusätzlichen
Parameter spezifizieren, von welchem Typ denn die einzelnen Elemente, die in diesem Puffer
denn zum Beispiel drin sind, ist ein Parameter, der groß T-Parameter. Dann haben wir ein N-Parameter
mit einer Default-Belegung 64, wo wir dann damit zum Ausdruck bringen wollen, wie groß unser Puffer
überhaupt dimunziert ist. Oder dann haben wir hier noch so eine andere Spezifikation, da sagen wir
dann einfach, okay diese Template-Klasse soll ein Monitor sein. Es ist also eine synchronisierte
Klasse, wenn man so will nach dem Monitor-Prinzip. Dann haben wir hier noch Bedingungsvariablen,
die wir verwenden werden, um denn die eigentlichen Einfüge- und Austragoperationen, also Put und Get,
denn aufeinander abzustimmen, also koordinieren zu können. So wie wir das in der letzten
Vorlesungsstunde eben an dem ersten Beispiel schon mal so ein Stück weit betrachtet haben. Okay,
denn würde hier bedeuten der Atomic-Bereich, da beginnt es denn eigentlich mit den nach außen hin
exportierten Prozeduren, mit den Monitor-Prozeduren. Und da haben wir gesagt, okay die sind eben
implizit automat. Das sind also hier öffentliche Prozeduren. Da haben wir den Konstruktor, der also
erst mal diese in- und out-Variablen, die Indexvariablen halt vorinitialisiert mit einem
Wert Null. Und dann haben wir die eigentlichen Operationen Put und Get mit Put, um dann immer
nach und nach ein Element in diesen Puffer einzutragen oder mit Get eben das nächste Element
aus dem Puffer halt herauszunehmen. Und wir wissen eben, dass das Eintragen in dem Puffer so
geschehen muss. Also konsistent, das heißt, jetzt soll kein Element, kein ein Puffereintrag
überschrieben werden. Ist der Puffer voll, muss der Prozess warten. Ist der Puffer leer, muss der
Prozess auch warten. Die Wartebedingungen für einen vollen Puffer haben wir jetzt hier so formuliert.
In plus eins modulo n ist gleich out. Die beiden Indexwerte stimmen überein. Denn soll das die
Wartebedingungen für den Prozess sein, der eine Put-Operation ausführt. Kein freier Pufferplatz,
er würde dann mit der Bedingungsvariablen Free arbeiten und darauf eine Wait-Operation ausführen.
Das heißt, er würde jetzt hier blockieren und so lange warten, bis eine Signalisierung zu diesem
Free halt geschieht. Diese Signalisierung kommt hier unten in der Zeile 19. Das heißt ja hier oben,
Zeile 11 von der Logik hier. Alle Puffereinträge sind belegt. Dann müssen wir mindestens eine
Get-Operation ausführen, dass ein Puffereintrag befreit wird. Also frei wird und das signalisieren
wir mit der Free-Operation. Vorher wird die Wartebedingungen für den Prozess, der hier oben
die Bedingungen formuliert hat, eben aufgehoben. Das ist dann entsprechend eigentlich genau diese
out plus plus Operation, die man hier unten im Get in der Zeile 18 durchführt. Wo wir nämlich
hier das nächste Element aus dem Puffer herauslesen und dann den Out-Index um eins erhöhen, letztendlich
entsprechend des Ring-Puffer-Konzepts. Nun bei der Get-Operation kann es eben auch eine Wartepedingung,
eine Wartesituation geben, in dem Fall, wo der Puffer nämlich leer ist. Da ist nichts drin und
ist genau dann der Fall, wie so ein Initialzustand hier in ist gleich out. Das wenn out gleich in
Index ist, da muss ein Prozess warten. Die inverse Operation dafür wäre denn die, dass
wenigstens ein, das erste Element in diesem Puffer eingetragen wird und das wird signalisiert mit der
Zeile 13, wo wir dann sagen Data-Signal. Da soll auf die Bedingungsvariable Data der nächste Prozess,
vielleicht auch alle Prozesse eben signalisiert werden, bereitgestellt werden, die möglicherweise
hier unten in der Get-Operation halt warten. Und wie gesagt, wartet hier kein Prozess,
dann geht das Signal verloren. Das gilt halt für diese Signaloperation in der Zeile 13,
denn wartet eben keiner im Get oder hier unten Zeile 19. Wenn hier keiner im Put wartet,
dann wird dieses Signal eben auch nicht gespeichert. Das ist das Grundprinzip. So einfach würde jetzt
unser bounded buffer halt aussehen können, wenn man so ein Monitorkonzept halt hat. Ein anderer
Presenters
Zugänglich über
Offener Zugang
Dauer
00:07:01 Min
Aufnahmedatum
2020-11-19
Hochgeladen am
2020-11-19 19:28:17
Sprache
de-DE